package Or

import (
	"encoding/json"
	"fmt"
	"db2s/gis"
	"db2s/parDef"
	"db2s/ref"
	"strings"
)

type Feature struct {
	Type       string          `json:"type"` // "Feature"
	Geometry   json.RawMessage `json:"geometry"`
	Properties map[string]any  `json:"properties,omitempty"`
	// 可选：持有 SRid 以便轴序判断（WKT/GeoJSON通常不含SRid，需要外部传入）
	SRid *int `json:"-"`
}
type FeatureCollection struct {
	Type     string    `json:"type"` // "FeatureCollection"
	Features []Feature `json:"features"`
}
type gisValQueryComposition struct {
	gisValue string
	rid      string
	axis     parDef.AxisOrder
	gType    string
}
type Coordinates struct {
	Type        string          `json:"type"`
	Coordinates json.RawMessage `json:"coordinates"`
}

func newSplitGisValue(s string) (res gisValQueryComposition) {
	if !strings.Contains(s, "@STYPE:") {
		return
	}
	qq := strings.Split(s, "@STYPE:")
	res.gisValue = qq[0]
	for k, v := range strings.Split(qq[1], ",") {
		if k == 0 {
			res.gType = v
			continue
		}
		switch {
		case strings.Contains(v, "SRID:"):
			if len(strings.ReplaceAll(v, "SRID:", "")) == 0 {
				res.rid = "0"
			} else {
				res.rid = strings.ReplaceAll(v, "SRID:", "")
			}
		case strings.Contains(v, "AXIS:"):
			var axis string
			if len(strings.ReplaceAll(v, "AXIS:", "")) == 0 {
				axis = "EPSG"
			} else {
				axis = strings.ReplaceAll(v, "AXIS:", "")
			}
			switch axis {
			case "EPSG":
				res.axis = parDef.AxisEPSG
			case "OGC":
				res.axis = parDef.AxisOGC
			}
		}
	}
	return
}
func gisPoints(geometry []byte) (coords []float64, err error) {
	var event = "[gisPoints]"
	if err = json.Unmarshal(geometry, &coords); err != nil {
		err = ref.ErrAddPrintf(event, fmt.Errorf("解析几何数据失败: %v", err))
		return
	}
	return
}
func gisLineString(geometry []byte) (coords [][]float64, err error) {
	var event = "[gisLineString]"
	if err = json.Unmarshal(geometry, &coords); err != nil {
		err = ref.ErrAddPrintf(event, fmt.Errorf("解析几何数据失败: %v", err))
		return
	}
	return
}
func gisPolygon(geometry []byte) (polygon any, err error) {
	var event = "[gisPolygon]"
	if err = json.Unmarshal(geometry, &polygon); err != nil {
		err = ref.ErrAddPrintf(event, fmt.Errorf("解析几何数据失败: %v", err))
		return
	}
	return
}
func gisMultiPoints(geometry []byte) (res any, err error) {
	var multiPoints any
	var event = "[gisMultiPoints]"
	if err = json.Unmarshal(geometry, &multiPoints); err != nil {
		err = ref.ErrAddPrintf(event, fmt.Errorf("解析几何数据失败: %v", err))
		return
	}
	res = multiPoints
	return
}
func gisMultiLineString(geometry []byte) (res any, err error) {
	var multiLineString any
	var event = "[gisMultiLineString]"
	if err = json.Unmarshal(geometry, &multiLineString); err != nil {
		err = ref.ErrAddPrintf(event, fmt.Errorf("解析几何数据失败: %v", err))
		return
	}
	res = multiLineString
	return
}
func gisMultiPolygon(geometry []byte) (res any, err error) {
	var multiPolygon any
	var event = "[gisMultiPolygon]"
	if err = json.Unmarshal(geometry, &multiPolygon); err != nil {
		err = ref.ErrAddPrintf(event, fmt.Errorf("解析几何数据失败: %v", err))
		return
	}
	res = multiPolygon
	return
}
func wktCoordinatesType(cc Coordinates) (wktJson parDef.GisValWkt, err error) {
	var gisVal = new(parDef.GisValWkt)
	var event = "[wktCoordinatesType]"
	switch cc.Type {
	case "Point":
		gisVal.Type = "point"
		if gisVal.Coords, err = gisPoints(cc.Coordinates); err != nil {
			err = ref.ErrAddPrintf(event, err)
			return
		}
	case "LineString":
		gisVal.Type = "lineString"
		if gisVal.Coords, err = gisLineString(cc.Coordinates); err != nil {
			err = ref.ErrAddPrintf(event, err)
			return
		}
	case "Polygon":
		gisVal.Type = "polygon"
		if gisVal.Coords, err = gisPolygon(cc.Coordinates); err != nil {
			err = ref.ErrAddPrintf(event, err)
			return
		}
	case "MultiPoint":
		gisVal.Type = "multiPoint"
		if gisVal.Coords, err = gisMultiPoints(cc.Coordinates); err != nil {
			err = ref.ErrAddPrintf(event, err)
			return
		}
	case "MultiLineString":
		gisVal.Type = "multiLineString"
		if gisVal.Coords, err = gisMultiLineString(cc.Coordinates); err != nil {
			err = ref.ErrAddPrintf(event, err)
			return
		}
	case "MultiPolygon":
		gisVal.Type = "multiPolygon"
		if gisVal.Coords, err = gisMultiPolygon(cc.Coordinates); err != nil {
			err = ref.ErrAddPrintf(event, err)
			return
		}
	}
	wktJson = *gisVal
	return
}
func getLatitude(Type string) (res int) {
	switch Type {
	case "2001", "2002", "2003", "2004", "2005", "2006", "2007":
		return 2
	case "3001", "3002", "3003", "3004", "3005", "3006", "3007":
		return 3
	default:
		return 2
	}
}
func GeomWktToJson(s any) (res string, err error) {
	var gisVal = new(parDef.GisValWkt)
	var feature = new(Feature)
	var gisValByte []byte
	gisValComposition := newSplitGisValue(s.(string))
	gisVal.SRid, gisVal.Axis = gisValComposition.rid, gisValComposition.axis
	gisVal.Latitude = getLatitude(gisValComposition.gType)
	if feature.Geometry, err = gis.GeomMarshalJson(gisValComposition.gisValue); err != nil {
		err = ref.ErrAddPrintf("Oracle.GeomWktToJson", err)
		return
	}
	var g struct {
		Type        string          `json:"type"`
		Coordinates json.RawMessage `json:"coordinates"`
		Geometries  json.RawMessage `json:"geometries"`
	}
	if err = json.Unmarshal(feature.Geometry, &g); err != nil {
		err = ref.ErrAddPrintf("Oracle.GeomWktToJson.json.Unmarshal", err)
		return
	}
	switch g.Type {
	case "GeometryCollection":
		gisVal.Type = "geometryCollection"
		var gg []Coordinates
		if err = json.Unmarshal(g.Geometries, &gg); err != nil {
			err = ref.ErrAddPrintf("GeomWktToJson", err)
		}
		var coordsG []parDef.GisValWkt
		for _, v := range gg {
			var coordsSubG parDef.GisValWkt
			if coordsSubG, err = wktCoordinatesType(v); err != nil {
				err = ref.ErrAddPrintf("Oracle.GeomWktToJson", err)
				return
			}
			coordsSubG.Latitude = gisVal.Latitude
			coordsG = append(coordsG, coordsSubG)
		}
		gisVal.Coords = coordsG
	default:
		var gg = Coordinates{
			Type:        g.Type,
			Coordinates: g.Coordinates,
		}
		var ggR parDef.GisValWkt
		if ggR, err = wktCoordinatesType(gg); err != nil {
			err = ref.ErrAddPrintf("Oracle.GeomWktToJson", err)
			return
		}
		gisVal.Type = ggR.Type
		gisVal.Coords = ggR.Coords
	}
	if gisValByte, err = json.Marshal(gisVal); err != nil {
		err = ref.ErrAddPrintf("Oracle.GeomWktToJson.json.Marshal", err)
		return
	}
	res = string(gisValByte)
	return
}
